package com.nuanshui.heatedloan.middleware.rabbitmq.annotation;

import java.lang.reflect.Method;
import java.util.HashMap;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Binding.DestinationType;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.listener.MethodRabbitListenerEndpoint;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.util.Assert;

import com.google.common.collect.Maps;

public class RabbitMessageHandlerAnnotationBeanPostProcessor implements BeanPostProcessor,BeanFactoryAware,SmartInitializingSingleton{

	
	private final RabbitListenerEndpointRegistrar registrar = new RabbitListenerEndpointRegistrar();

	private final RabbitHandlerMethodFactoryAdapter messageHandlerMethodFactory =
			new RabbitHandlerMethodFactoryAdapter();
	private RabbitListenerEndpointRegistry endpointRegistry;
	
	private BeanFactory beanFactory;
	
	
	public RabbitListenerEndpointRegistry getEndpointRegistry() {
		return endpointRegistry;
	}

	public void setEndpointRegistry(RabbitListenerEndpointRegistry endpointRegistry) {
		this.endpointRegistry = endpointRegistry;
	}

	public RabbitListenerEndpointRegistrar getRegistrar() {
		return registrar;
	}

	public BeanFactory getBeanFactory() {
		return beanFactory;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		//获取对象的class
		Class<?> targetClass = AopUtils.getTargetClass(bean);
		//获取class加的 MqMessageHandler 注解
		MqMessageHandler messageHandler =  AnnotationUtils.getAnnotation(targetClass, MqMessageHandler.class);
		if(messageHandler!=null)  {//只有加了此注解的才进行 相关处理
			//获取rabbitAdmin （此类是对rabbitmq管理类  ，queue 以及exchange都是基于这个类进行声明的）
			RabbitAdmin rabbitAdmin =  this.beanFactory.getBean(RabbitAdmin.class);
			//创建一个queue
			Queue queue = new Queue(messageHandler.value(),true,false,false);
			//对此queue进行声明
			rabbitAdmin.declareQueue(queue);
			
			//对queue 和 exchange 进行绑定  
			HashMap<String, Object> args = Maps.newHashMap();
			Binding binding = new Binding(messageHandler.value(), DestinationType.QUEUE, "directExchange",messageHandler.value(),args);
			rabbitAdmin.declareBinding(binding);
			
			//创建消息队列监听器（）
			MethodRabbitListenerEndpoint endpoint = new MethodRabbitListenerEndpoint();
			endpoint.setBean(bean);//监听器的对象
			endpoint.setMethod(getOnMessageMethod(targetClass));//监听器的方法
			endpoint.setId(messageHandler.value());
			endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
			endpoint.setQueueNames(messageHandler.value());//监听那个 队列
			endpoint.setAdmin(rabbitAdmin);
            RabbitListenerContainerFactory<?> factory = this.beanFactory.getBean(RabbitListenerContainerFactory.class);
            this.registrar.registerEndpoint(endpoint, factory);//进行注册
		}
		return bean;
	}
	
	
	private Method getOnMessageMethod(Class<?> targetClass) {
		Method methods[] =  targetClass.getMethods();
		for(Method method :methods) {
			if("onMessage".equals(method.getName())) {
				return method;
			}
		}
		return null;
		
	}

	@Override
	public void afterSingletonsInstantiated() {
		this.registrar.setBeanFactory(this.beanFactory);
		
		if (this.registrar.getEndpointRegistry() == null) {
			if (this.endpointRegistry == null) {
				Assert.state(this.beanFactory != null,
						"BeanFactory must be set to find endpoint registry by bean name");
				this.endpointRegistry = this.beanFactory.getBean(
						RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME,
						RabbitListenerEndpointRegistry.class);
			}
			this.registrar.setEndpointRegistry(this.endpointRegistry);
		}

		
		MessageHandlerMethodFactory handlerMethodFactory = this.registrar.getMessageHandlerMethodFactory();
		if (handlerMethodFactory != null) {
			this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(handlerMethodFactory);
		}
		this.registrar.afterPropertiesSet();

	}
	
	
	/**
	 * An {@link MessageHandlerMethodFactory} adapter that offers a configurable underlying
	 * instance to use. Useful if the factory to use is determined once the endpoints
	 * have been registered but not created yet.
	 * @see RabbitListenerEndpointRegistrar#setMessageHandlerMethodFactory
	 */
	private class RabbitHandlerMethodFactoryAdapter implements MessageHandlerMethodFactory {

		private MessageHandlerMethodFactory messageHandlerMethodFactory;

		public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory rabbitHandlerMethodFactory1) {
			this.messageHandlerMethodFactory = rabbitHandlerMethodFactory1;
		}

		@Override
		public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
			return getMessageHandlerMethodFactory().createInvocableHandlerMethod(bean, method);
		}

		private MessageHandlerMethodFactory getMessageHandlerMethodFactory() {
			if (this.messageHandlerMethodFactory == null) {
				this.messageHandlerMethodFactory = createDefaultMessageHandlerMethodFactory();
			}
			return this.messageHandlerMethodFactory;
		}

		private MessageHandlerMethodFactory createDefaultMessageHandlerMethodFactory() {
			DefaultMessageHandlerMethodFactory defaultFactory = new DefaultMessageHandlerMethodFactory();
			defaultFactory.setBeanFactory(beanFactory);
			defaultFactory.afterPropertiesSet();
			return defaultFactory;
		}

	}

}
